package com.fingard.dsp.bank.directbank.chinapay04;

import org.apache.commons.lang.StringUtils;

import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util.CertUtil;
import com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util.SecureUtil;
import com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util.Util;
import com.fingard.io.FileHelper;
import com.fingard.net.WebRequest;
import com.fingard.text.StringHelper;

import java.io.*;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import static com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util.Constants.*;
import static com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util.Util.coverMap2String;
import static com.fingard.dsp.bank.directbank.chinapay04.ChinaPay04Util.Util.getRequestParamString;

public class ChinaPay04Base extends DirectBase {

    protected static String FILEFIELDUNION = "|";
    protected static String FILELINEUNION = "\r\n";
    protected static String FILELINEUNION_BODY = "\r\n";

    protected String getCharset() {
        return getBankConfig().getCharset("UTF-8");
    }

    /**
     * 过滤请求报文中的空字符串或者空字符串
     *
     * @param contentData
     * @return
     */
    public Map<String, String> filterBlank(Map<String, String> contentData) {
        WriteBankLogLn("打印请求报文域 :");
        Map<String, String> submitFromData = new HashMap<String, String>();
        Set<String> keyset = contentData.keySet();

        for (String key : keyset) {
            String value = contentData.get(key);
            if (StringUtils.isNotBlank(value)) {
                // 对value值进行去除前后空处理
                submitFromData.put(key, value.trim());
                WriteBankLogLn(key + "-->" + String.valueOf(value));
            }
        }
        return submitFromData;
    }

    /**
     * 请求报文签名(使用配置文件中配置的私钥证书或者对称密钥签名)<br>
     * 功能：对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
     */
    public Map<String, String> signWithFilterBlank(String p_act, Map<String, String> reqData, StringBuilder p_sb) throws Exception {
        reqData = filterBlank(reqData);
        sign(p_act, reqData, p_sb);
        return reqData;
    }

    public boolean sign(String p_act, Map<String, String> data, StringBuilder p_sb) throws Exception {

        String encoding = getCharset();
        String signMethod = data.get(param_signMethod);
        String version = data.get(param_version);
        if (!VERSION_1_0_0.equals(version) && !VERSION_5_0_1.equals(version) && StringHelper.isNullOrEmpty(signMethod)) {
            WriteBankLogLn("signMethod must Not null");
            p_sb.append("signMethod must Not null");
            return false;
        }

        if (StringHelper.isNullOrEmpty(version)) {
            WriteBankLogLn("version must Not null");
            p_sb.append("version must Not null");
            return false;
        }
        CertUtil tmpCert = new CertUtil(p_act, reqHeader, sbLog);
        if (SIGNMETHOD_RSA.equals(signMethod) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
            if (VERSION_5_0_0.equals(version) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
                // 设置签名证书序列号
                data.put(param_certId, tmpCert.getSignCertId());
                // 将Map信息转换成key1=value1&key2=value2的形式
                String stringData = coverMap2String(data);
                WriteBankLogLn("待签名请求报文串:[" + stringData + "]");
                byte[] byteSign = null;
                String stringSign = null;
                try {
                    // 通过SHA1进行摘要并转16进制
                    byte[] signDigest = SecureUtil
                            .sha1X16(stringData, encoding);
                    byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft(
                            tmpCert.getSignCertPrivateKey(), signDigest));
                    stringSign = new String(byteSign);
                    // 设置签名域值
                    data.put(param_signature, stringSign);
                    return true;
                } catch (Exception e) {
                    WriteBankLogLn("Sign Error", e);
                    p_sb.append("Sign Error:" + e.getMessage());
                    return false;
                }
            } else if (VERSION_5_1_0.equals(version)) {
                // 设置签名证书序列号
                data.put(param_certId, tmpCert.getSignCertId());
                // 将Map信息转换成key1=value1&key2=value2的形式
                String stringData = coverMap2String(data);
                WriteBankLogLn("待签名请求报文串:[" + stringData + "]");
                byte[] byteSign = null;
                String stringSign = null;
                try {
                    // 通过SHA256进行摘要并转16进制
                    byte[] signDigest = SecureUtil
                            .sha256X16(stringData, encoding);
                    byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft256(
                            tmpCert.getSignCertPrivateKey(), signDigest));
                    stringSign = new String(byteSign);
                    // 设置签名域值
                    data.put(param_signature, stringSign);
                    return true;
                } catch (Exception e) {
                    WriteBankLogLn("Sign Error", e);
                    p_sb.append("Sign Error:" + e.getMessage());
                    return false;
                }
            }
        }
        return false;
    }

    /**
     * 验证签名
     *
     * @param resData 返回报文数据
     * @return
     */
    public boolean validate(String p_act, Map<String, String> resData, StringBuilder p_sb) throws Exception {
        WriteBankLogLn("验签处理开始");
        String encoding = getCharset();
        String signMethod = resData.get(param_signMethod);
        String version = resData.get(param_version);
        CertUtil tmpCert = new CertUtil(p_act, reqHeader, sbLog);
        if (SIGNMETHOD_RSA.equals(signMethod) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
            // 获取返回报文的版本号
            if (VERSION_5_0_0.equals(version) || VERSION_1_0_0.equals(version) || VERSION_5_0_1.equals(version)) {
                String stringSign = resData.get(param_signature);
                WriteBankLogLn("签名原文：[" + stringSign + "]");
                // 从返回报文中获取certId ，然后去证书静态Map中查询对应验签证书对象
                String certId = resData.get(param_certId);
                WriteBankLogLn("对返回报文串验签使用的验签公钥序列号：[" + certId + "]");
                // 将Map信息转换成key1=value1&key2=value2的形式
                String stringData = coverMap2String(resData);
                WriteBankLogLn("待验签返回报文串：[" + stringData + "]");
                try {
                    // 验证签名需要用银联发给商户的公钥证书.
                    return SecureUtil.validateSignBySoft(tmpCert
                                    .getValidatePublicKey(certId), SecureUtil
                                    .base64Decode(stringSign.getBytes(encoding)),
                            SecureUtil.sha1X16(stringData, encoding));
                } catch (UnsupportedEncodingException e) {
                    WriteBankLogLn(e.getMessage(), e);
                } catch (Exception e) {
                    WriteBankLogLn(e.getMessage(), e);
                }
            } else if (VERSION_5_1_0.equals(version)) {
                // 1.从返回报文中获取公钥信息转换成公钥对象
                String strCert = resData.get(param_signPubKeyCert);
//				LogUtil.writeLog("验签公钥证书：["+strCert+"]");
                X509Certificate x509Cert = tmpCert.genCertificateByStr(strCert);
                if (x509Cert == null) {
                    WriteBankLogLn("convert signPubKeyCert failed");
                    p_sb.append("convert signPubKeyCert failed");
                    return false;
                }
                // 2.验证证书链
                if (!tmpCert.verifyCertificate(x509Cert)) {
                    WriteBankLogLn("验证公钥证书失败，证书信息：[" + strCert + "]");
                    p_sb.append("验证公钥证书失败，证书信息：[" + strCert + "]");
                    return false;
                }

                // 3.验签
                String stringSign = resData.get(param_signature);
                WriteBankLogLn("签名原文：[" + stringSign + "]");
                // 将Map信息转换成key1=value1&key2=value2的形式
                String stringData = coverMap2String(resData);
                WriteBankLogLn("待验签返回报文串：[" + stringData + "]");
                try {
                    // 验证签名需要用银联发给商户的公钥证书.
                    boolean result = SecureUtil.validateSignBySoft256(x509Cert
                            .getPublicKey(), SecureUtil.base64Decode(stringSign
                            .getBytes(encoding)), SecureUtil.sha256X16(
                            stringData, encoding));
                    WriteBankLogLn("验证签名" + (result ? "成功" : "失败"));
                    return result;
                } catch (UnsupportedEncodingException e) {
                    WriteBankLogLn(e.getMessage(), e);
                } catch (Exception e) {
                    WriteBankLogLn(e.getMessage(), e);
                }
            }

        } else if (SIGNMETHOD_SHA256.equals(signMethod)) {
            // 1.进行SHA256验证
            String stringSign = resData.get(param_signature);
            WriteBankLogLn("签名原文：[" + stringSign + "]");
            // 将Map信息转换成key1=value1&key2=value2的形式
            String stringData = coverMap2String(resData);
            WriteBankLogLn("待验签返回报文串：[" + stringData + "]");
            String strBeforeSha256 = stringData
                    + AMPERSAND
                    + SecureUtil.sha256X16Str(FileHelper.readFileText(tmpCert.actItem.ownCertFilePath, encoding), encoding);
            String strAfterSha256 = SecureUtil.sha256X16Str(strBeforeSha256,
                    encoding);
            boolean result = stringSign.equals(strAfterSha256);
            WriteBankLogLn("验证签名" + (result ? "成功" : "失败"));
            return result;
        }
        return false;
    }

    /**
     * 功能：将批量文件内容使用DEFLATE压缩算法压缩，Base64编码生成字符串并返回<br>
     * 适用到的交易：批量代付，批量代收，批量退货<br>
     *
     * @param filePath 批量文件-全路径文件名<br>
     * @return
     */
    public String enCodeFileContent(String filePath, String encoding) {
        String baseFileContent = "";

        File file = new File(filePath);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                WriteBankLogLn(e.getMessage(), e);
            }
        }
        InputStream in = null;
        try {
            in = new FileInputStream(file);
            int fl = in.available();
            if (null != in) {
                byte[] s = new byte[fl];
                in.read(s, 0, fl);
                // 压缩编码.
                baseFileContent = new String(SecureUtil.base64Encode(Util.deflater(s)), encoding);
            }
        } catch (Exception e) {
            WriteBankLogLn(e.getMessage(), e);
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    WriteBankLogLn(e.getMessage(), e);
                }
            }
        }
        return baseFileContent;
    }

    /**
     * 功能：解析交易返回的fileContent字符串并落地 （ 解base64，解DEFLATE压缩并落地）<br>
     * 适用到的交易：对账文件下载，批量交易状态查询<br>
     *
     * @param resData       返回报文map<br>
     * @param fileDirectory 落地的文件目录（绝对路径）
     */
    public String deCodeFileContent(Map<String, String> resData, String fileDirectory) {
        // 解析返回文件
        String filePath = null;
        String fileContent = resData.get(param_fileContent);
        if (null != fileContent && !"".equals(fileContent)) {
            FileOutputStream out = null;
            try {
                byte[] fileArray = Util.inflater(SecureUtil
                        .base64Decode(fileContent.getBytes(getCharset())));
                if (StringHelper.isNullOrEmpty(resData.get("fileName"))) {
                    filePath = fileDirectory + File.separator + resData.get("merId")
                            + "_" + resData.get("batchNo") + "_"
                            + resData.get("txnTime") + ".txt";
                } else {
                    filePath = fileDirectory + File.separator + resData.get("fileName");
                }
                File file = new File(filePath);
                if (file.exists()) {
                    file.delete();
                }
                file.createNewFile();
                out = new FileOutputStream(file);
                out.write(fileArray, 0, fileArray.length);
                out.flush();
            } catch (UnsupportedEncodingException e) {
                WriteBankLogLn(e.getMessage(), e);
            } catch (IOException e) {
                WriteBankLogLn(e.getMessage(), e);
            } finally {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return filePath;
    }

    /**
     * 功能：将结果文件内容 转换成明文字符串：解base64,解压缩<br>
     * 适用到的交易：批量交易状态查询<br>
     *
     * @param fileContent 批量交易状态查询返回的文件内容<br>
     * @return 内容明文<br>
     */
    public String getFileContent(String fileContent, String encoding) {
        String fc = "";
        try {
            fc = new String(Util.inflater(SecureUtil.base64Decode(fileContent.getBytes())), encoding);
        } catch (UnsupportedEncodingException e) {
            WriteBankLogLn(e.getMessage(), e);
        } catch (IOException e) {
            WriteBankLogLn(e.getMessage(), e);
        }
        return fc;
    }

    public List<String> unzip(String zipFilePath, String outPutDirectory) {
        List<String> fileList = new ArrayList<String>();
        BufferedOutputStream bout = null;
        FileOutputStream fos = null;
        try {
            ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFilePath));//输入源zip路径
            BufferedInputStream bin = new BufferedInputStream(zin);
            File file = null;
            ZipEntry entry;
            try {
                while ((entry = zin.getNextEntry()) != null && !entry.isDirectory()) {
                    file = new File(outPutDirectory, entry.getName());
                    if (!file.exists()) {
                        (new File(file.getParent())).mkdirs();
                    }
                    fos = new FileOutputStream(file);
                    bout = new BufferedOutputStream(fos);
                    int b;
                    while ((b = bin.read()) != -1) {
                        bout.write(b);
                    }
                    bout.flush();
                    fileList.add(file.getAbsolutePath());
                    System.out.println(file + "解压成功");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    bin.close();
                    zin.close();
                    if (bout != null) {
                        bout.close();
                    }
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return fileList;
    }

    /**
     * 功能：解析全渠道商户对账文件中的ZM文件并以List<Map>方式返回
     * 适用交易：对账文件下载后对文件的查看
     *
     * @param filePath ZM文件全路径
     * @return 包含每一笔交易中 序列号 和 值 的map序列
     */
    public static List<Map> parseZMFile(String filePath) throws IOException {
        int lengthArray[] = {3, 11, 11, 6, 10, 19, 12, 4, 2, 21, 2, 32, 2, 6, 10, 13, 13, 4, 15, 2, 2, 6, 2, 4, 32, 1, 21, 15, 1, 15, 32, 13, 13, 8, 32, 13, 13, 12, 2, 1, 32, 98};
        return parseFile(filePath, lengthArray);
    }

    /**
     * 功能：解析全渠道商户对账文件中的ZME文件并以List<Map>方式返回
     * 适用交易：对账文件下载后对文件的查看
     *
     * @param filePath ZME文件全路径
     * @return 包含每一笔交易中 序列号 和 值 的map序列
     */
    public static List<Map> parseZMEFile(String filePath) throws IOException {
        int lengthArray[] = {3, 11, 11, 6, 10, 19, 12, 4, 2, 2, 6, 10, 4, 12, 13, 13, 15, 15, 1, 12, 2, 135};
        return parseFile(filePath, lengthArray);
    }

    /**
     * 功能：解析全渠道商户 ZM,ZME对账文件
     *
     * @param filePath
     * @param lengthArray 参照《全渠道平台接入接口规范 第3部分 文件接口》 全渠道商户对账文件 6.1 ZM文件和6.2 ZME 文件 格式的类型长度组成int型数组
     * @return
     */
    private static List<Map> parseFile(String filePath, int lengthArray[]) throws IOException {
        List<Map> ZmDataList = new ArrayList<Map>();
        InputStreamReader read = null;
        FileInputStream fis = null;
        BufferedReader bufferedReader = null;
        try {
            String encoding = "UTF-8";
            File file = new File(filePath);
            if (file.isFile() && file.exists()) { //判断文件是否存在
                fis = new FileInputStream(file);
                read = new InputStreamReader(fis, encoding);//考虑到编码格式
                bufferedReader = new BufferedReader(read);
                String lineTxt = null;
                while ((lineTxt = bufferedReader.readLine()) != null) {
                    //解析的结果MAP，key为对账文件列序号，value为解析的值
                    Map<Integer, String> ZmDataMap = new LinkedHashMap<Integer, String>();
                    //左侧游标
                    int leftIndex = 0;
                    //右侧游标
                    int rightIndex = 0;
                    for (int i = 1; i <= lengthArray.length; i++) {
                        rightIndex = leftIndex + lengthArray[i];
                        String filed = lineTxt.substring(leftIndex, rightIndex);
                        leftIndex = rightIndex + 1;
                        ZmDataMap.put(i, filed);
                    }
                    ZmDataList.add(ZmDataMap);
                }
                read.close();
            } else {
                System.out.println("找不到指定的文件");
            }
        } catch (Exception e) {
            System.out.println("读取文件内容出错");
            e.printStackTrace();
        } finally {
            if (read != null) {
                read.close();
            }
            if (fis != null) {
                fis.close();
            }
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        }

        return ZmDataList;
    }


    protected String[] sendToBank(Map<String, String> reqData, String reqUrl) throws Exception {
        String[] retStr = new String[]{"", ""};
        WriteBankLogLn("2.提交银行的地址：" + reqUrl);
        String charsetName = getCharset();
        String reqStr = getRequestParamString(reqData, charsetName);
        WriteBankLogStep2(reqStr);

        //发送银行前置
        WebRequest tmpRequest = new WebRequest(reqUrl);
        String charName = getCharset();
        byte[] tmpWriteBytes = reqStr.getBytes(charsetName);
        retStr = tmpRequest.upload(tmpWriteBytes, charName);
        WriteBankLogStep3(retStr[1]);
        return retStr;
    }
}
